package top.jafar.data.structures.expression;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

/**
 * 算术表达式计算
 */
public class ArithmeticOperator extends ExpressionOperator<String> {
    public ArithmeticOperator() {

    }

    public String execute(String expression) {
        return calcByAntiPolishNotation(expression);
    }

    /**
     * 逆波兰表达式计算结果
     * @param expression
     * @return
     */
    private String calcByAntiPolishNotation(String expression) {
        String antiPolishNotationExpress = getAntiPolishNotationExpress(expression);
        // 8 5 - 6 * 2 * 3 + 2 +
        // "3+2*(6*(8-5))+2"
        System.out.println("计算出的逆波兰表达式: "+antiPolishNotationExpress);
        String[] antiPolishNotationItems = antiPolishNotationExpress.split(" ");
        Stack<String> stack = new Stack<>();
        for (String item : antiPolishNotationItems) {
            if (isOperator(item)) {
                // 操作符
                char operator = item.charAt(0);
                String rightNum = stack.pop();
                String leftNum = stack.pop();
                String result = doCalculation(operator, leftNum, rightNum);
                stack.push(result);
            }else {
                stack.push(item);
            }
        }
        return stack.pop();
    }


    /**
     * 递归实现中缀表达式计算结果
     * @param expression
     * @return
     */
    private String calcByRecursion(String expression) {
        if (expression != null && expression.trim().length() > 0) {
            // 数字栈
            Stack<String> numberStack = new Stack<>();
            // 符号栈
            Stack<Character> operatorStack = new Stack<>();
            // 遍历字符串
            StringBuffer buffer = new StringBuffer();
            // 当前是否处于括号中
            int bracketsWrapCount = 0;
            Stack<String> brackets = new Stack<>();
            for (int i = 0; i < expression.length(); i++) {
                char c = expression.charAt(i);
                if (isLeftBrackets(c)) {
                    // 括号开始
                    if (bracketsWrapCount > 0) {
                        // 如果当前处于第二级括号
                        brackets.push(buffer.toString());
                        buffer.setLength(0);
                    }
                    bracketsWrapCount++;
                } else if (isRightBrackets(c)) {
                    // 括号结束
                    // 默认当前为最内一层的括号
                    // 计算出表达式结果,
                    String tempStr = buffer.length() == 0 ? brackets.pop() : buffer.toString();
                    buffer.setLength(0);
                    String result = execute(tempStr);
                    if (!brackets.isEmpty()) {
                        // 如果括号栈中不为空
                        // 将结果拼接到最后一个元素中去
                        brackets.push(brackets.pop() + result);
                        buffer.setLength(0);
                    } else {
                        // 括号栈中已经没有数据了将结果放入数字栈去
                        buffer.setLength(0);
                        buffer.append(result);
                    }
                    bracketsWrapCount--;
                } else if (bracketsWrapCount > 0) {
                    // 处在括号中
                    buffer.append(c);
                } else if (isArithmeticOperator(c)) {
                    // 加减法直接放
                    String curNum = buffer.toString();
                    buffer.setLength(0);
                    char operator = c;
                    if (isAddOrSubtract(operator)) {
                        // 如果是加减法，切上次的符号是乘除法，那么就先行计算
                        while (true) {
                            if (operatorStack.isEmpty()) {
                                break;
                            }
                            char lastOperator = operatorStack.pop();
                            if (isMultiplyOrDivide(lastOperator)) {
                                if (numberStack.isEmpty()) {
                                    break;
                                }
                                String leftStr = numberStack.pop();
                                String rightStr = curNum;
                                curNum = doCalculation(lastOperator, leftStr, rightStr);
                            }else {
                                operatorStack.push(lastOperator);
                                break;
                            }
                        }
                    }
                    numberStack.push(curNum);
                    operatorStack.push(operator);
                } else {
                    buffer.append(c);
                }
            }
            if (buffer.length() > 0) {
                numberStack.push(buffer.toString());
                buffer.setLength(0);
            }

            // 解析完毕
            // 开始执行
            String resultNum = numberStack.pop();
            while (true) {
                if (numberStack.isEmpty()) {
                    break;
                }
                String leftNum = numberStack.pop();
                char operator = operatorStack.pop();
                resultNum = doCalculation(operator, leftNum, resultNum);
            }
            return resultNum;
        }
        return "0";
    }

    // 四则运算
    public String doCalculation(char operator, String ...args) {
        try {
            if (args.length < 2) {
                throw new RuntimeException("参数个数不对");
            }
            String left = args[0];
            String right = args[1];
            BigDecimal leftNum = new BigDecimal(left);
            BigDecimal rightNum = new BigDecimal(right);
            MathContext context = new MathContext(2, RoundingMode.HALF_EVEN);
            switch (operator) {
                case '+':
                    return leftNum.add(rightNum).toString();
                case '-':
                    return leftNum.subtract(rightNum).toString();
                case '*':
                    return leftNum.multiply(rightNum, context).toString();
                case '/':
                    return leftNum.divide(rightNum, context).toString();
            }
            throw new RuntimeException("不支持的操作符号: " + operator);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    @Override
    protected String getAntiPolishNotationExpress(String express) {
        if (express == null) {
            return null;
        }
        // 逆波兰需要两个Stack转换
        // 默认情况下操作符放入stack1
        Stack<String> stack1 = new Stack<>();
        // 操作数放入stack2
        Stack<String> stack2 = new Stack<>();
        // 需要对express进行拆解
        // 用于存放除符号外的其他字符
        ItemStringBuilder tmp = new ItemStringBuilder();
        char[] expressChars = express.toCharArray();
        // 数组
        List<String> expressItemList = new ArrayList<>();
        // 将其切分成数字和符号按空格分割
        for (char ch : expressChars) {
            if (isBrackets(ch) || isOperator(ch)) {
                // 操作符
                if (!tmp.isEmpty()) {
                    expressItemList.add(tmp.getString());
                }
                expressItemList.add(String.valueOf(ch));
            } else {
                tmp.append(ch);
            }
        }
        if (!tmp.isEmpty()) {
            expressItemList.add(tmp.getString());
        }
        // 遍历表达式
        for (String item: expressItemList) {
            if (isOperator(item)) {
                // 这里是操作符号
                // 如果是操作符先判断stack1中的是否为空
                if (stack1.empty()) {
                    // 如果stack1为空，直接入栈
                    stack1.push(item);
                }else {
                    // 获取上一次的操作符
                    while (true) {
                        if (stack1.empty()) {
                            break;
                        }
                        String lastOp = stack1.peek();
                        if (operatorLessThan(item, lastOp) || operatorEquals(item, lastOp)) {
                            // 如果当前操作符操作顺序小于上一次的那么将操作符放到stack2中去
                            stack2.push(stack1.pop());
                            stack1.push(item);
                            break;
                        } else {
                            // - 转
                            // 如果不是那么将操作符放入stack1中去
                            stack1.push(item);
                            break;
                        }
                    }
                }
            } else if (isBrackets(item)) {
                // 这里是括号
                if (isLeftBrackets(item)) {
                    // 左括号直接入栈
                    stack1.push(item);
                } else {
                    // 右括号
                    while (true) {
                        // 挨个出站
                        if (stack1.empty()) {
                            break;
                        }
                        String pop = stack1.pop();
                        if (!isLeftBrackets(pop.charAt(0))) {
                            stack2.push(pop);
                        }else if(isLeftBrackets(pop.charAt(0))) {
                            break;
                        }
                    }
                }
            } else {
                // 这里是非操作符或括号直接放入缓存
                stack2.push(item);
            }
        }
        // 将stack1中的所有元素放到stack2中去
        while (!stack1.empty()) {
            stack2.push(stack1.pop());
        }

        // 完成之后将stack2翻转一下就可以了
        Iterator<String> iterator = stack2.iterator();
        List<String> polishNotationItems = new ArrayList<>();
        while (iterator.hasNext()) {
            polishNotationItems.add(iterator.next());
        }
        // 逆波兰表达式
        StringBuilder antiPolishNotationExpress = new StringBuilder();
        for (int i = 0; i < polishNotationItems.size(); i++) {
            antiPolishNotationExpress.append(polishNotationItems.get(i));
            if (i < polishNotationItems.size()-1) {
                antiPolishNotationExpress.append(" ");
            }
        }
        return antiPolishNotationExpress.toString();
    }

    @Override
    protected boolean isOperator(char ch) {
        return isArithmeticOperator(ch);
    }

    @Override
    protected boolean isOperator(String ch) {
        return isArithmeticOperator(ch);
    }

    public boolean operatorEquals(String leftOp, String rightOp) {
        return (isAddOrSubtract(leftOp) && isAddOrSubtract(rightOp))
                || (isMultiplyOrDivide(leftOp) && isMultiplyOrDivide(rightOp));
    }

    public boolean operatorGreaterThan(String leftOp, String rightOp) {
        // 左边的运算符是否大于右边的运算符
        return isBrackets(leftOp)
                || (isMultiplyOrDivide(leftOp) && isAddOrSubtract(rightOp));
    }

    public boolean operatorLessThan(String leftOp, String rightOp) {
        // 上次的符号非括号, 当前是加减，右边是乘除的情况下为真
        return !isBrackets(leftOp) && isAddOrSubtract(leftOp) && isMultiplyOrDivide(rightOp);
    }

    /**
     * 是否四则运算符号
     * @param operator
     * @return
     */
    public boolean isArithmeticOperator(char operator) {
        return isAddOrSubtract(operator) || isMultiplyOrDivide(operator);
    }
    public boolean isArithmeticOperator(String operator) {
        return isAddOrSubtract(operator) || isMultiplyOrDivide(operator);
    }

    /**
     * 是否加或减号
     * @param operator
     * @return
     */
    public boolean isAddOrSubtract(char operator) {
        return operator == '+' || operator == '-';
    }
    public boolean isAddOrSubtract(String operator) {
        return "+".equals(operator) || "-".equals(operator);
    }

    /**
     * 是否乘或除号
     * @param operator
     * @return
     */
    public boolean isMultiplyOrDivide(char operator) {
        return operator == '*' || operator == '/';
    }
    public boolean isMultiplyOrDivide(String operator) {
        return "*".equals(operator) || "/".equals(operator);
    }

}